package net.sk.china.city.talk.service.impl.user;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import net.sk.china.city.talk.constants.Constants;
import net.sk.china.city.talk.constants.RegularConstants;
import net.sk.china.city.talk.dao.user.UserDao;
import net.sk.china.city.talk.model.user.UserBill;
import net.sk.china.city.talk.model.user.UserIntegralRecord;
import net.sk.china.city.talk.request.user.*;
import net.sk.china.city.talk.response.user.TopDetailResponse;
import net.sk.china.city.talk.response.user.UserIntegralRecordResponse;
import net.sk.china.city.talk.response.user.UserResponse;
import net.sk.china.city.talk.service.order.StoreOrderService;
import net.sk.china.city.talk.service.user.*;
import net.sk.china.common.exception.TalkException;
import net.sk.china.common.model.user.User;
import net.sk.china.common.request.PageParamRequest;
import net.sk.china.common.response.CommonPage;
import net.sk.china.common.response.DateLimit;
import net.sk.china.common.utils.DateUtil;
import net.sk.china.common.utils.TalkUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.support.TransactionTemplate;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * UserService 接口实现
 * +----------------------------------------------------------------------
 * // | Talk To [ 聊来改善地区金融服务环境，助力企业发展 ]
 * // +----------------------------------------------------------------------
 * // | Copyright (c) 2018-2024 聊来 All rights reserved.
 * // +----------------------------------------------------------------------
 * // | Licensed ( <a href="http://www.apache.org/licenses/LICENSE-2.0">apache</a> )
 * // +----------------------------------------------------------------------
 * // | @Author: 聊来 <18970881148@qq.com>
 * // +----------------------------------------------------------------------
 * // | DateTime: 2024/6/20 14:41
 * // +----------------------------------------------------------------------
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserDao, User> implements UserService {

    @Resource
    private UserDao dao;

    private final TransactionTemplate transactionTemplate;

    private final UserGroupService userGroupService;

    private final UserTagService userTagService;

    private final UserIntegralRecordService userIntegralRecordService;

    private final UserBillService userBillService;

    private final StoreOrderService storeOrderService;

    @Autowired
    public UserServiceImpl(TransactionTemplate transactionTemplate, UserGroupService userGroupService, @Lazy UserTagService userTagService,
                           @Lazy UserIntegralRecordService userIntegralRecordService, UserBillService userBillService, @Lazy StoreOrderService storeOrderService) {
        this.transactionTemplate = transactionTemplate;
        this.userGroupService = userGroupService;
        this.userTagService = userTagService;
        this.userIntegralRecordService = userIntegralRecordService;
        this.userBillService = userBillService;
        this.storeOrderService = storeOrderService;
    }


    /**
     * 根据账号查询用户
     * @param account 账号
     * @return {@code User}
     */
    @Override
    public User getByAccount(String account) {
        LambdaQueryWrapper<User> query = new LambdaQueryWrapper<>();
        query.eq(User::getAccount, account);
        query.last("limit 1");

        return dao.selectOne(query);
    }

    /**
     * 根据手机号查询用户
     * @param phone 用户手机号
     * @return {@code User}
     */
    @Override
    public User getByPhone(String phone) {
        LambdaQueryWrapper<User> query = new LambdaQueryWrapper<>();
        query.eq(User::getPhone, phone);
        query.last("limit 1");

        return dao.selectOne(query);
    }

    /**
     * 根据日期获取注册用户数量
     * @param date 日期
     * @return {@code Long}
     */
    @Override
    public Long getRegisterNumByDate(String date) {
        QueryWrapper<User> query = new QueryWrapper<>();
        query.select("uid");
        query.apply("date_format(create_time, '%Y-%m-%d') = {0}", date);
        
        return dao.selectCount(query);
    }

    /**
     * 用户统计曲线图
     * @param date 查询日期
     * @return {@code Map<Object, Object>}
     */
    @Override
    public Map<Object, Object> getAddUserCountGroupDate(String date) {
        Map<Object, Object> map = new HashMap<>(64);
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.select("count(uid) as uid", "left(create_time, 10) as create_time");
        if (StringUtils.isNotBlank(date)) {
            DateLimit dateLimit = DateUtil.getDateLimit(date);
            queryWrapper.between("create_time", dateLimit.getStartTime(), dateLimit.getEndTime());
        }
        queryWrapper.groupBy("left(create_time, 10)").orderByAsc("create_time");
        List<User> list = dao.selectList(queryWrapper);
        if (list.size() < 1) {
            return map;
        }

        for (User user : list) {
            map.put(DateUtil.localDateTimeToString(user.getCreateTime(), Constants.DATE_FORMAT_DATE), user.getUid());
        }
        return map;
    }

    /**
     * 清除User Group id
     * @param groupId 待清除的GroupId
     */
    @Override
    public void clearGroupByGroupId(String groupId) {
        LambdaUpdateWrapper<User> query = Wrappers.lambdaUpdate();
        query.set(User::getGroupId, "").eq(User::getGroupId, groupId);
        update(query);
    }


    /**
     * 用户分页列表
     * @param request 查询参数
     * @param pageParamRequest 分页参数
     * @return {@code PageInfo<UserResponse>}
     */
    @Override
    public PageInfo<UserResponse> getList(UserSearchRequest request, PageParamRequest pageParamRequest) {
        Page<User> pageUser = PageHelper.startPage(pageParamRequest.getPage(), pageParamRequest.getLimit());
        Map<String, Object> map = new HashMap<>();

        if (!StringUtils.isBlank(request.getGroupId())) {
            map.put("groupId", request.getGroupId());
        }

        if (!StringUtils.isBlank(request.getLabelId())) {
            String tagIdSql = TalkUtil.getFindInSetSql("u.tag_id", request.getLabelId());
            map.put("tagIdSql", tagIdSql);
        }

        if (StringUtils.isNotBlank(request.getUserType())) {
            map.put("userType", request.getUserType());
        }

        if (StringUtils.isNotBlank(request.getSex())) {
            map.put("sex", Integer.parseInt(request.getSex()));
        }

        if (StringUtils.isNotBlank(request.getCountry())) {
            map.put("country", request.getCountry());
            // 根据省市查询
            if (StrUtil.isNotBlank(request.getCity())) {
                request.setProvince(request.getProvince().replace("省", ""));
                request.setCity(request.getCity().replace("市", ""));
                map.put("address", request.getProvince() + "," + request.getCity());
            }
        }

        if (StrUtil.isNotBlank(request.getPayCount())) {
            map.put("payCount", Integer.parseInt(request.getPayCount()));
        }

        if (request.getStatus() != null) {
            map.put("status", request.getStatus() ? 1 : 0);
        }

        DateLimit dateLimit = DateUtil.getDateLimit(request.getDateLimit());

        if (!StringUtils.isBlank(dateLimit.getStartTime())) {
            map.put("startTime", dateLimit.getStartTime());
            map.put("endTime", dateLimit.getEndTime());
            map.put("accessType", request.getAccessType());
        }
        if (request.getKeywords() != null) {
            map.put("keywords", request.getKeywords());
        }
        List<User> userList = dao.findAdminList(map);
        List<UserResponse> userResponses = new ArrayList<>();
        for (User user : userList) {
            UserResponse userResponse = new UserResponse();
            BeanUtils.copyProperties(user, userResponse);
            // 获取分组信息
            if (!StringUtils.isBlank(user.getGroupId())) {
                userResponse.setGroupName(userGroupService.getGroupNameInId(user.getGroupId()));
                userResponse.setGroupId(user.getGroupId());
            }

            // 获取标签信息
            if (!StringUtils.isBlank(user.getTagId())) {
                userResponse.setTagName(userTagService.getTagNameInId(user.getTagId()));
                userResponse.setTagId(user.getTagId());
            }

            userResponses.add(userResponse);
        }
        return CommonPage.copyPageInfo(pageUser, userResponses);
    }


    /**
     * 更新用户
     * @param userRequest 用户参数
     * @return {@code Boolean}
     */
    @Override
    public Boolean updateUser(UserUpdateRequest userRequest) {
        User tempUser = getById(userRequest.getUid());
        User user = new User();
        BeanUtils.copyProperties(userRequest, user);

        return updateById(user);
    }

    /**
     * 后台修改用户手机号
     * @param id 用户uid
     * @param phone 手机号
     * @return {@code Boolean}
     */
    @Override
    public Boolean updateUserPhone(Integer id, String phone) {
        boolean matchPhone = ReUtil.isMatch(RegularConstants.PHONE_TWO, phone);
        if (!matchPhone) {
            throw new TalkException("手机号格式错误，请输入正确得手机号");
        }
        User user = getById(id);
        if (ObjectUtil.isNull(user)) {
            throw new TalkException("对应用户不存在");
        }
        if (phone.equals(user.getPhone())) {
            throw new TalkException("手机号与之前一致");
        }

        //检测当前手机号是否已经是账号
        User tempUser = getByPhone(phone);
        if (ObjectUtil.isNotNull(tempUser)) {
            throw new TalkException("此手机号码已被注册");
        }

        User newUser = new User();
        newUser.setUid(id);
        newUser.setPhone(phone);
        newUser.setAccount(phone);
        return dao.updateById(newUser) > 0;
    }

    /**
     * 获取用户详情
     * @param id 用户uid
     * @return {@code User}
     */
    @Override
    public User getInfoByUid(Integer id) {
        User user = getById(id);
        if (ObjectUtil.isNull(user)) {
            throw new TalkException("用户不存在");
        }
        return user;
    }

    /**
     * 根据参数类型查询会员对应的信息
     * @param userId Integer 会员id
     * @param type int 类型 0=消费记录，1=积分明细，4=余额变动
     * @param pageParamRequest PageParamRequest 分页
     * @return {@code Object}
     */
    @Override
    public Object getInfoByCondition(Integer userId, Integer type, PageParamRequest pageParamRequest) {
        switch (type) {
            case 0:
                return storeOrderService.findPaidListByUid(userId, pageParamRequest);
            case 1:
                AdminIntegralSearchRequest a = new AdminIntegralSearchRequest();
                a.setUid(userId);
                PageInfo<UserIntegralRecordResponse> res = userIntegralRecordService.findAdminList(a, pageParamRequest);
                return res.getList();
            case 4:
                FundsMonitorSearchRequest b = new FundsMonitorSearchRequest();
                b.setUid(userId);
                b.setCategory(Constants.USER_BILL_CATEGORY_MONEY);
                return userBillService.getList(b, pageParamRequest);
            default:
                break;
        }

        return new ArrayList<>();
    }

    /**
     * 根据昵称匹配用户，返回id集合
     * @param nikeName 需要匹配得昵称
     * @return {@code List<Integer>}
     */
    @Override
    public List<Integer> findIdListLikeName(String nikeName) {
        LambdaQueryWrapper<User> query = Wrappers.lambdaQuery();
        query.select(User::getUid);
        query.like(User::getNickname, nikeName);
        List<User> userList = dao.selectList(query);
        if (CollUtil.isEmpty(userList)) {
            return new ArrayList<>();
        }
        return userList.stream().map(User::getUid).collect(Collectors.toList());
    }

    /**
     * 会员详情页Top数据
     * @param userId 用户uid
     * @return {@code TopDetailResponse}
     */
    @Override
    public TopDetailResponse getTopDetail(Integer userId) {
        TopDetailResponse topDetail = new TopDetailResponse();
        User currentUser = dao.selectById(userId);
        topDetail.setUser(currentUser);
        topDetail.setBalance(currentUser.getNowMoney());
        topDetail.setIntegralCount(currentUser.getIntegral());
        topDetail.setMothConsumeCount(BigDecimal.valueOf(0));
        topDetail.setAllConsumeCount(BigDecimal.valueOf(0));
        topDetail.setMothOrderCount(0);
        topDetail.setAllOrderCount(0);
        return topDetail;
    }

    /**
     * 操作积分、余额
     * @param request 请求数据
     * @return {@code Boolean}
     */
    @Override
    public Boolean updateIntegralMoney(UserOperateIntegralMoneyRequest request) {
        if (ObjectUtil.isNull(request.getMoneyValue()) || ObjectUtil.isNull(request.getIntegralValue())) {
            throw new TalkException("至少输入一个金额");
        }
        if (request.getMoneyValue().compareTo(BigDecimal.ZERO) < 1 && request.getIntegralValue() <= 0) {
            throw new TalkException("修改值不能等小于等于0");
        }

        User user = getById(request.getUid());
        if (ObjectUtil.isNull(user)) {
            throw new TalkException("用户不存在");
        }
        // 减少时要判断小于0的情况,添加时判断是否超过数据限制
        if (request.getMoneyType().equals(2) && request.getMoneyValue().compareTo(BigDecimal.ZERO) != 0) {
            if (user.getNowMoney().subtract(request.getMoneyValue()).compareTo(BigDecimal.ZERO) < 0) {
                throw new TalkException("余额扣减后不能小于0");
            }
        }
        if (request.getMoneyType().equals(1) && request.getMoneyValue().compareTo(BigDecimal.ZERO) != 0) {
            if (user.getNowMoney().add(request.getMoneyValue()).compareTo(new BigDecimal("99999999.99")) > 0) {
                throw new TalkException("余额添加后后不能大于99999999.99");
            }
        }

        if (request.getIntegralType().equals(2) && request.getIntegralValue() != 0) {
            if (user.getIntegral() - request.getIntegralValue() < 0) {
                throw new TalkException("积分扣减后不能小于0");
            }
        }
        if (request.getIntegralType().equals(1) && request.getIntegralValue() != 0) {
            if ((user.getIntegral() + request.getIntegralValue()) > 99999999) {
                throw new TalkException("积分添加后不能大于99999999");
            }
        }

        Boolean execute = transactionTemplate.execute(e -> {
            // 处理余额
            if (request.getMoneyValue().compareTo(BigDecimal.ZERO) > 0) {
                // 生成UserBill
                UserBill userBill = new UserBill();
                userBill.setUid(user.getUid());
                userBill.setLinkId("0");
                userBill.setTitle("后台操作");
                userBill.setCategory(Constants.USER_BILL_CATEGORY_MONEY);
                userBill.setNumber(request.getMoneyValue());
                userBill.setStatus(1);
                userBill.setCreateTime(DateUtil.getNowTime());

                if (request.getMoneyType() == 1) {// 增加
                    userBill.setPm(1);
                    userBill.setType(Constants.USER_BILL_TYPE_SYSTEM_ADD);
                    userBill.setBalance(user.getNowMoney().add(request.getMoneyValue()));
                    userBill.setMark(StrUtil.format("后台操作增加了{}余额", request.getMoneyValue()));

                    userBillService.save(userBill);
                    operationNowMoney(user.getUid(), request.getMoneyValue(), user.getNowMoney(), "add");
                } else {
                    userBill.setPm(0);
                    userBill.setType(Constants.USER_BILL_TYPE_SYSTEM_SUB);
                    userBill.setBalance(user.getNowMoney().subtract(request.getMoneyValue()));
                    userBill.setMark(StrUtil.format("后台操作减少了{}余额", request.getMoneyValue()));

                    userBillService.save(userBill);
                    operationNowMoney(user.getUid(), request.getMoneyValue(), user.getNowMoney(), "sub");
                }
            }

            // 处理积分
            if (request.getIntegralValue() > 0) {
                // 生成记录
                UserIntegralRecord integralRecord = new UserIntegralRecord();
                integralRecord.setUid(user.getUid());
                integralRecord.setLinkType(Constants.INTEGRAL_RECORD_LINK_TYPE_SIGN);
                integralRecord.setTitle(Constants.BROKERAGE_RECORD_TITLE_SYSTEM);
                integralRecord.setIntegral(request.getIntegralValue());
                integralRecord.setStatus(Constants.INTEGRAL_RECORD_STATUS_COMPLETE);
                if (request.getIntegralType() == 1) {// 增加
                    // 最后的实际积分、权益包额度
                    int integral = user.getIntegral() + request.getIntegralValue();
                    integralRecord.setType(Constants.INTEGRAL_RECORD_TYPE_ADD);
                    integralRecord.setBalance(new BigDecimal(String.valueOf(integral)));
                    integralRecord.setMark(StrUtil.format("后台操作增加了{}积分", request.getIntegralValue()));
                    operationIntegral(user.getUid(), request.getIntegralValue(), user.getIntegral(), "add");
                } else {
                    int integral = user.getIntegral() - request.getIntegralValue();
                    integralRecord.setType(Constants.INTEGRAL_RECORD_TYPE_SUB);
                    integralRecord.setBalance(new BigDecimal(String.valueOf(integral)));
                    integralRecord.setMark(StrUtil.format("后台操作减少了{}积分", request.getIntegralValue()));
                    operationIntegral(user.getUid(), request.getIntegralValue(), user.getIntegral(), "sub");
                }
                userIntegralRecordService.save(integralRecord);
            }
            return Boolean.TRUE;
        });


        return ObjectUtil.isNull(execute) ? Boolean.FALSE : execute;
    }


    /**
     * 会员分组
     * @param id String id
     * @param groupId Integer 分组Id
     * @return {@code Boolean}
     */
    @Override
    public Boolean group(String id, String groupId) {
        if (StrUtil.isBlank(id)) {
            throw new TalkException("会员编号不能为空");
        }
        if (StrUtil.isBlank(groupId)) {
            throw new TalkException("分组id不能为空");
        }

        //循环id处理
        List<Integer> idList = TalkUtil.stringToArray(id);
        idList = idList.stream().distinct().collect(Collectors.toList());
        List<User> list = getListInUid(idList);
        if (CollUtil.isEmpty(list)) {
            throw new TalkException("没有找到用户信息");
        }
        if (list.size() < idList.size()) {
            throw new TalkException("没有找到用户信息");
        }
        for (User user : list) {
            user.setGroupId(groupId);
        }
        return updateBatchById(list);
    }

    /**
     * 添加、扣减积分（权益包额度）
     * @param uid 用户id
     * @param integral 积分
     * @param oldIntegral 历史积分
     * @param type 类型：add—添加，sub—扣减
     * @return {@code Boolean}
     */
    @Override
    public Boolean operationIntegral(Integer uid, Integer integral, Integer oldIntegral, String type) {
        UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
        String add = "add";
        if (type.equals(add)) {
            updateWrapper.setSql(StrUtil.format("integral = integral + {}", integral));
        } else {
            updateWrapper.setSql(StrUtil.format("integral = integral - {}", integral));
            updateWrapper.last(StrUtil.format(" and (integral - {} >= 0)", integral));
        }
        updateWrapper.eq("uid", uid);
        updateWrapper.eq("integral", oldIntegral);
        return update(updateWrapper);
    }

    /**
     * 添加/扣减余额
     * @param uid 用户id
     * @param price 金额
     * @param oldMoney 历史金额
     * @param type 类型：add—添加，sub—扣减
     * @return {@code Boolean}
     */
    @Override
    public Boolean operationNowMoney(Integer uid, BigDecimal price, BigDecimal oldMoney, String type) {
        UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
        String add = "add";
        if (type.equals(add)) {
            updateWrapper.setSql(StrUtil.format("now_money = now_money + {}", price));
        } else {
            updateWrapper.setSql(StrUtil.format("now_money = now_money - {}", price));
            updateWrapper.last(StrUtil.format(" and (now_money - {} >= 0)", price));
        }
        updateWrapper.eq("uid", uid);
        updateWrapper.eq("now_money", oldMoney);
        return update(updateWrapper);
    }
    /**
     * 会员标签
     * @param id String id
     * @param tagId Integer 标签Id
     * @return Boolean
     */
    @Override
    public Boolean tag(String id, String tagId) {
        if (StrUtil.isBlank(id)) {
            throw new TalkException("会员编号不能为空");
        }
        if (StrUtil.isBlank(tagId)) {
            throw new TalkException("标签id不能为空");
        }

        //循环id处理
        List<Integer> idList = TalkUtil.stringToArray(id);
        idList = idList.stream().distinct().collect(Collectors.toList());
        List<User> list = getListInUid(idList);
        if (CollUtil.isEmpty(list)) {
            throw new TalkException("没有找到用户信息");
        }
        if (list.size() < 1) {
            throw new TalkException("没有找到用户信息");
        }
        for (User user : list) {
            user.setTagId(tagId);
        }
        return updateBatchById(list);
    }


    /**
     * 根据用户id获取用户列表 map模式
     * @param uidList uidList
     * @return {@code HashMap<Integer, User>}
     */
    @Override
    public HashMap<Integer, User> getMapListInUid(List<Integer> uidList) {
        List<User> userList = getListInUid(uidList);
        return getMapByList(userList);
    }

    /**
     * 根据用户id获取用户列表 map模式
     * @param list 用户List
     * @return {@code HashMap<Integer, User>}
     */
    private HashMap<Integer, User> getMapByList(List<User> list) {
        HashMap<Integer, User> map = new HashMap<>(16);
        if (null == list || list.size() < 1) {
            return map;
        }

        for (User user : list) {
            map.put(user.getUid(), user);
        }

        return map;
    }

    /**
     * 用户id in list
     * @param uidList List<Integer> id
     * @return {@code List<User>}
     */
    private List<User> getListInUid(List<Integer> uidList) {
        LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.in(User::getUid, uidList);
        return dao.selectList(lambdaQueryWrapper);
    }
}
